home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / util / misc / Fudgit233.lha / Source / src / help.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-16  |  23.9 KB  |  842 lines

  1. /* GNUPLOT - help.c
  2.  *
  3.  * Copyright (C) 1986, 1987, 1990, 1991   Thomas Williams, Colin Kelley
  4.  *
  5.  * Permission to use, copy, and distribute this software and its
  6.  * documentation for any purpose with or without fee is hereby granted,
  7.  * provided that the above copyright notice appear in all copies and
  8.  * that both that copyright notice and this permission notice appear
  9.  * in supporting documentation.
  10.  *
  11.  * Permission to modify the software is granted, but not the right to
  12.  * distribute the modified code.  Modifications are to be distributed
  13.  * as patches to released version.
  14.  * 
  15.  * This software is provided "as is" without express or implied warranty.
  16.  *
  17.  *
  18.  * AUTHORS
  19.  *
  20.  *   Original Software:
  21.  *     Thomas Williams,  Colin Kelley.
  22.  *
  23.  *   Gnuplot 2.0 additions:
  24.  *       Russell Lang, Dave Kotz, John Campbell.
  25.  *
  26.  *   Gnuplot 3.0 additions:
  27.  *       Gershon Elber and many others.
  28.  *
  29.  * Send your comments or suggestions to
  30.  *  pixar!info-gnuplot@sun.com.
  31.  * This is a mailing list; to join it send a note to
  32.  *  pixar!info-gnuplot-request@sun.com. 
  33.  * Send bug reports to
  34.  *  pixar!bug-gnuplot@sun.com.
  35.  */
  36.  
  37. #include <stdio.h>
  38. #include <string.h>
  39. #ifndef NOSTDLIB_H
  40. #include <stdlib.h>
  41. #endif
  42. #ifndef NOMALLOC_H
  43. #include <malloc.h>
  44. #endif
  45.  
  46.  
  47. extern int errno;
  48. extern char *getenv(const char *);
  49. extern FILE *fopen(const char *, const char *);
  50. static int instring(char *str, char c);
  51.  
  52. #define    SAME    0    /* for strcmp() */
  53.  
  54. #include "help.h"    /* values passed back */
  55.  
  56. /* help -- help subsystem that understands defined keywords
  57. **
  58. ** Looks for the desired keyword in the help file at runtime, so you
  59. ** can give extra help or supply local customizations by merely editing
  60. ** the help file.
  61. **
  62. ** The original (single-file) idea and algorithm is by John D. Johnson,
  63. ** Hewlett-Packard Company.  Thanx and a tip of the Hatlo hat!
  64. **
  65. ** Much extension by David Kotz for use in gnutex, and then in gnuplot.
  66. ** Added output paging support, both unix and builtin. Rewrote completely
  67. ** to read helpfile into memory, avoiding reread of help file. 12/89.
  68. **
  69. ** Modified by Russell Lang to avoid reading completely into memory
  70. ** if MSDOS defined.  This uses much less memory.  6/91
  71. **
  72. ** The help file looks like this (the question marks are really in column 1):
  73. **
  74. **     ?topic
  75. **     This line is printed when the user wants help on "topic".
  76. **     ?keyword
  77. **     ?Keyword
  78. **     ?KEYWORD
  79. **     These lines will be printed on the screen if the user wanted
  80. **     help on "keyword", "Keyword", or "KEYWORD".  No casefolding is
  81. **    done on the keywords.
  82. **     ?subject
  83. **     ?alias
  84. **     This line is printed for help on "subject" and "alias".
  85. **     ? or double? ( avoid trigraph)
  86. **    
  87. **     Since there is a null keyword for this line, this section
  88. **     is printed when the user wants general help (when a help
  89. **     keyword isn't given).  A command summary is usually here.
  90. **    Notice that the null keyword is equivalent to a "?" keyword
  91. **    here, because of the '?' and 'double ?' topic lines above.
  92. **   If multiple keywords are given, the first is considered the
  93. **   'primary' keyword. This affects a listing of available topics.
  94. **     ?last-subject
  95. **     Note that help sections are terminated by the start of the next
  96. **     '?' entry or by EOF.  So you can't have a leading '?' on a line
  97. **     of any help section.  You can re-define the magic character to
  98. **    recognize in column 1, though, if '?' is too useful.  (Try ^A.)
  99. */
  100.  
  101. #define    KEYFLAG    '?'    /* leading char in help file topic lines */
  102.  
  103. /*
  104. ** Calling sequence:
  105. **    int result;        # 0 == success
  106. **    char *keyword;        # topic to give help on
  107. **    char *pathname;        # path of help file
  108. **      int subtopics;        # set to TRUE if only subtopics to be listed
  109. **                # returns TRUE if subtopics were found
  110. **    result = help(keyword, pathname, &subtopics);
  111. ** Sample:
  112. **    cmd = "search\n";
  113. **    helpfile = "/usr/local/lib/program/program.help";
  114. **    subtopics = FALSE;
  115. **    if (help(cmd, helpfile, &subtopics) != H_FOUND)
  116. **        printf("Sorry, no help for %s", cmd);
  117. **
  118. **
  119. ** Speed this up by replacing the stdio calls with open/close/read/write.
  120. */
  121. #ifdef    WDLEN
  122. #  define    PATHSIZE    WDLEN
  123. #else
  124. #  define    PATHSIZE    BUFSIZ
  125. #endif
  126.  
  127. typedef int boolean;
  128. #ifndef TRUE
  129. #define TRUE (1)
  130. #define FALSE (0)
  131. #endif
  132.  
  133. typedef struct line_s LINEBUF;
  134. struct line_s {
  135.     char *line;            /* the text of this line */
  136.     LINEBUF *next;            /* the next line */
  137. };
  138.  
  139. typedef struct linkey_s LINKEY;
  140. struct linkey_s {
  141.     char *key;                /* the name of this key */
  142.     long pos;                /* ftell position */
  143.     LINEBUF *text;            /* the text for this key */
  144.     boolean primary;        /* TRUE -> is a primary name for a text block */
  145.     LINKEY *next;            /* the next key in linked list */
  146. };
  147.  
  148. typedef struct key_s KEY;
  149. struct key_s {
  150.     char *key;                /* the name of this key */
  151.     long pos;                /* ftell position */
  152.     LINEBUF *text;            /* the text for this key */
  153.     boolean primary;        /* TRUE -> is a primary name for a text block */
  154. };
  155. static LINKEY *keylist = NULL;    /* linked list of keys */
  156. static KEY *keys = NULL;        /* array of keys */
  157. static int keycount = 0;        /* number of keys */
  158. static FILE *helpfp = NULL;
  159.  
  160. static int LoadHelp(char *path);
  161. static void sortkeys(void);
  162. static int keycomp(const void *a, const void *b);
  163. static LINEBUF *storeline(char *text);
  164. static LINKEY *storekey(char *key);
  165. static KEY *FindHelp(char *keyword);
  166. static boolean Ambiguous(KEY *key, int len);
  167.  
  168. /* Help output */
  169. static void PrintHelp(KEY *key, boolean *subtopics);
  170. static void ShowSubtopics(KEY *key, boolean *subtopics);
  171. static void StartOutput(void);
  172. static void OutLine(char *line);
  173. static void EndOutput(void);
  174. static FILE *help_stream;        /* for unix pager, if any */
  175. static int pagelines;        /* count for builtin pager */
  176. #define SCREENSIZE 24        /* lines on screen (most have at least 24) */
  177.  
  178. /* help:
  179.  * print a help message
  180.  * also print available subtopics, if subtopics is TRUE
  181.  */
  182.  
  183.  
  184. /*
  185. #if !defined(ultrix) && !defined(__STDC__)
  186. ULTRIX compiler crashes on this line, and it is well it should...
  187. extern void qsort (void *, int, int, int (*) (KEY *, KEY *));
  188. #endif
  189. */
  190.  
  191. static int help(char *keyword, char *path, boolean *subtopics)
  192.                       /* on this topic */
  193.                        /* from this file */
  194.                        /* (in) - subtopics only? */
  195.                         /* (out) - are there subtopics? */
  196. {
  197.     static char oldpath[PATHSIZE] = "";    /* previous help file */
  198.     int status;            /* result of LoadHelp */
  199.     KEY *key;            /* key that matches keyword */
  200.  
  201.     /*
  202.     ** Load the help file if necessary (say, first time we enter this routine,
  203.     ** or if the help file changes from the last time we were called).
  204.     ** Also may occur if in-memory copy was freed.
  205.     ** Calling routine may access errno to determine cause of H_ERROR.
  206.     */
  207.     errno = 0;
  208.     if (strncmp(oldpath, path, PATHSIZE) != SAME)
  209.      Ft_FreeHelp();
  210.     if (keys == NULL) {
  211.        status = LoadHelp(path);
  212.        if (status == H_ERROR)
  213.         return(status);
  214.  
  215.        /* save the new path in oldpath */
  216.        if (strlen(path) < PATHSIZE)
  217.         (void) strcpy(oldpath, path);
  218.        else {                /* not enough room in oldpath, sigh */
  219.           (void) strncpy(oldpath, path, PATHSIZE - 1);
  220.           oldpath[PATHSIZE - 1] = '\0';
  221.        }
  222.     }
  223.  
  224.     /* look for the keyword in the help file */
  225.     key = FindHelp(keyword);
  226.     if (key != NULL) {
  227.        /* found the keyword: print help and return */
  228.        PrintHelp(key, subtopics);
  229.        status = H_FOUND;
  230.     } else {
  231.        status = H_NOTFOUND;
  232.     }
  233.  
  234.     return(status);
  235. }
  236.  
  237. /* we only read the file once, into memory
  238.  * except for MSDOS when we don't read all the file -
  239.  * just the keys and location of the text
  240.  */
  241. static int
  242. LoadHelp(char *path)
  243. {
  244.     LINKEY *key=NULL;        /* this key */
  245.     long pos=0;              /* ftell location within help file */
  246.     char buf[BUFSIZ];        /* line from help file */
  247.     LINEBUF *head;           /* head of text list  */
  248.     LINEBUF *firsthead = NULL;
  249.     boolean primary;        /* first ? line of a set is primary */
  250.     boolean flag;
  251.  
  252.     if ((helpfp = fopen(path, "r")) == NULL) {
  253.        /* can't open help file, so error exit */
  254.        return (H_ERROR);
  255.     }
  256.  
  257.     /*
  258.     ** The help file is open.  Look in there for the keyword.
  259.     */
  260.     (void) fgets(buf, BUFSIZ - 1, helpfp);
  261.     while (!feof(helpfp)) {
  262.        /*
  263.         ** Make an entry for each synonym keyword
  264.         */
  265.        primary = TRUE;
  266.        while (buf[0] == KEYFLAG) {
  267.           key = storekey(buf+1);    /* store this key */
  268.           key->primary = primary;
  269.           key->text = NULL;            /* fill in with real value later */
  270.           key->pos = 0;                /* fill in with real value later */
  271.           primary = FALSE;
  272.           pos = ftell(helpfp);
  273.           if (fgets(buf, BUFSIZ - 1, helpfp) == (char *)NULL)
  274.             break;
  275.        }
  276.        /*
  277.         ** Now store the text for this entry.
  278.         ** buf already contains the first line of text.
  279.         */
  280. #ifndef MSDOS
  281.        firsthead = storeline(buf);
  282.        head = firsthead;
  283. #endif
  284.        while ( (fgets(buf, BUFSIZ - 1, helpfp) != (char *)NULL)
  285.         && (buf[0] != KEYFLAG) ){
  286. #ifndef MSDOS
  287.           /* save text line */
  288.           head->next = storeline(buf);
  289.           head = head->next;
  290. #endif
  291.        }
  292.        /* make each synonym key point to the same text */
  293.        do {
  294.           key->pos = pos;
  295.           key->text = firsthead;
  296.           flag = key->primary;
  297.           key = key->next;
  298.        } while ( flag!=TRUE  &&  key!=NULL );
  299.     }
  300. #ifndef MSDOS
  301.     (void) fclose(helpfp);
  302. #endif
  303.  
  304.     /* we sort the keys so we can use binary search later */
  305.     sortkeys();
  306.     return(H_FOUND); /* ok */
  307. }
  308.  
  309. /* make a new line buffer and save this string there */
  310. static LINEBUF *
  311. storeline(char *text)
  312. {
  313.     LINEBUF *new;
  314.  
  315.     new = (LINEBUF *)malloc(sizeof(LINEBUF));
  316.     if (new == NULL) {
  317.         fprintf(stderr, "help: not enough memory to store help file.\n");
  318.         return(new);
  319.     }
  320.     if (text != NULL) {
  321.         new->line = (char *) malloc((unsigned int)(strlen(text)+1));
  322.         if (new->line == NULL) {
  323.             fprintf(stderr, "help: not enough memory to store help file.\n");
  324.             free(new);
  325.             return(NULL);
  326.         }
  327.         (void) strcpy(new->line, text);
  328.     } else {
  329.         new->line = NULL;
  330.     }
  331.     new->next = NULL;
  332.  
  333.     return(new);
  334. }
  335.  
  336. /* Add this keyword to the keys list, with the given text */
  337. static LINKEY *
  338. storekey(char *key)
  339. {
  340.     LINKEY *new;
  341.  
  342.     key[strlen(key)-1] = '\0'; /* cut off \n  */
  343.  
  344.     new = (LINKEY *)malloc(sizeof(LINKEY));
  345.     if (new == NULL) {
  346.         fprintf(stderr, "help: not enough memory to store help file.\n");
  347.         return(NULL);
  348.     }
  349.     new->key = (char *) malloc((unsigned int)(strlen(key)+1));
  350.     if (new->key == NULL) {
  351.         fprintf(stderr, "help: not enough memory to store help file.\n");
  352.         free(new);
  353.         return(NULL);
  354.     }
  355.     (void) strcpy(new->key, key);
  356.  
  357.     /* add to front of list */
  358.     new->next = keylist;
  359.     keylist = new;
  360.     keycount++;
  361.     return(new);
  362. }
  363.  
  364. /* we sort the keys so we can use binary search later */
  365. /* We have a linked list of keys and the number.
  366.  * to sort them we need an array, so we reform them into an array,
  367.  * and then throw away the list.
  368.  */
  369. static void
  370. sortkeys(void)
  371. {
  372.     LINKEY *p,*n;            /* pointers to linked list */
  373.     int i;                /* index into key array */
  374.    
  375.     /* allocate the array */
  376.     keys = (KEY *)malloc((unsigned int)((keycount+1) * sizeof(KEY)));
  377.     if (keys == NULL) {
  378.         fprintf(stderr, "help: not enough memory to store help file.\n");
  379.         return;
  380.     }
  381.     /* copy info from list to array, freeing list */
  382.     for (p = keylist, i = 0; p != NULL; p = n, i++) {
  383.        keys[i].key = p->key;
  384.        keys[i].pos = p->pos;
  385.        keys[i].text = p->text;
  386.        keys[i].primary = p->primary;
  387.        n = p->next;
  388.        free( (char *)p );
  389.     }
  390.  
  391.     /* a null entry to terminate subtopic searches */
  392.     keys[keycount].key = NULL;
  393.     keys[keycount].pos = 0;
  394.     keys[keycount].text = NULL;
  395.  
  396.     /* sort the array */
  397.     /* note that it only moves objects of size (two pointers + long + int) */
  398.     /* it moves no strings */
  399.     qsort((char *)keys, keycount, sizeof(KEY), keycomp);
  400.     return;
  401. }
  402.  
  403. static int
  404. keycomp(void const *a, void const *b)
  405. {
  406.     return (strcmp(((KEY *)a)->key, ((KEY*)b)->key));
  407. }
  408.  
  409. /* Free the help file from memory. */
  410. /* May be called externally if space is needed */
  411. void
  412. Ft_FreeHelp(void)
  413. {
  414.     int i;                /* index into keys[] */
  415.     LINEBUF *t, *next;
  416.  
  417.     if (keys == NULL)
  418.      return;
  419.  
  420.     for (i = 0; i < keycount; i++) {
  421.        free( (char *)keys[i].key );
  422.        if (keys[i].primary)   /* only try to release text once! */
  423.        for (t = keys[i].text; t != NULL; t = next) {
  424.           free( (char *)t->line );
  425.           next = t->next;
  426.           free( (char *)t );
  427.        }
  428.     }
  429.     free( (char *)keys );
  430.     keys = NULL;
  431.     keycount = 0;
  432. #ifdef MSDOS
  433.     (void) fclose(helpfp);
  434. #endif
  435. }
  436.  
  437. /* FindHelp:
  438.  *  Find the key that matches the keyword.
  439.  *  The keys[] array is sorted by key.
  440.  *  We could use a binary search, but a linear search will aid our
  441.  *  attempt to allow abbreviations. We search for the first thing that
  442.  *  matches all the text we're given. If not an exact match, then
  443.  *  it is an abbreviated match, and there must be no other abbreviated
  444.  *  matches -- for if there are, the abbreviation is ambiguous.
  445.  *  We print the ambiguous matches in that case, and return not found.
  446.  */
  447. static KEY *                /* NULL if not found */
  448. FindHelp(char *keyword)
  449.                               /* string we look for */
  450. {
  451.     KEY *key;
  452.     int len = strlen(keyword);
  453.     int compare;
  454.  
  455.     /**** print the whole linked list
  456.     for (key = keys; key->key != NULL ; key++)
  457.         fprintf(stderr, "%s\n", key->key);
  458.     ****/
  459.     if (!len)
  460.         return(keys);
  461.     for (key = keys, compare = 1; key->key != NULL && compare > 0; key++) {
  462.        compare = strncmp(keyword, key->key, len);
  463.        if (compare == 0)    /* we have a match! */
  464.         if (!Ambiguous(key, len)) {
  465.             /* non-ambiguous abbreviation */
  466.             (void) strcpy(keyword, key->key); /* give back the full spelling */
  467.             return(key);        /* found!! */
  468.         }
  469.     }
  470.  
  471.     /* not found, or ambiguous */
  472.     return(NULL);
  473. }
  474.  
  475. /* Ambiguous:
  476.  * Check the key for ambiguity up to the given length.
  477.  * It is ambiguous if it is not a complete string and there are other
  478.  * keys following it with the same leading substring.
  479.  */
  480. static boolean
  481. Ambiguous(KEY *key, int len)
  482. {
  483.     char *first;
  484.     char *prev;
  485.     boolean status = FALSE;    /* assume not ambiguous */
  486.     int compare;
  487.     int sublen;
  488.  
  489.     if (key->key[len] == '\0')
  490.      return(FALSE);
  491.    
  492.     for (prev = first = key->key, compare = 0, key++;
  493.         key->key != NULL && compare == 0; key++) {
  494.        compare = strncmp(first, key->key, len);
  495.        /***
  496.        fprintf(stderr, "Compare %s with %s: %d\n", first, key->key, !compare);
  497.        ***/
  498.        if (compare == 0) {
  499.           /* So this key matches the first one, up to len.
  500.            * But is it different enough from the previous one
  501.            * to bother printing it as a separate choice?
  502.            */
  503.           sublen = instring(prev+len, ' ');
  504.           if (strncmp(key->key, prev, len+sublen+1) != 0) {
  505.              /* yup, this is different up to the next space */
  506.              if (!status) {
  507.                 /* first one we have printed is special */
  508.                 fprintf(stderr,
  509.                        "Ambiguous request '%.*s'; possible matches:\n",
  510.                        len, first);
  511.                 fprintf(stderr, "\t%s\n", prev);
  512.                 status = TRUE;
  513.              }
  514.              fprintf(stderr, "\t%s\n", key->key);
  515.              prev = key->key;
  516.           }
  517.        }
  518.     }
  519.    
  520.     return(status);
  521. }
  522.  
  523. /* PrintHelp:
  524.  * print the text for key
  525.  */
  526.  /* (in) - subtopics only? */
  527.  /* (out) - are there subtopics? */
  528. static void
  529. PrintHelp(KEY *key, boolean *subtopics)
  530. {
  531.     LINEBUF *t;
  532. #ifdef MSDOS
  533.     char buf[BUFSIZ];        /* line from help file */
  534. #endif
  535.  
  536.     StartOutput();
  537.  
  538.     if (subtopics == NULL || !*subtopics) {
  539. #ifdef MSDOS
  540.        fseek(helpfp,key->pos,0);
  541.        while ( (fgets(buf, BUFSIZ - 1, helpfp) != (char *)NULL)
  542.             && (buf[0] != KEYFLAG) ) {
  543.           OutLine(buf);
  544.        }
  545. #else
  546.        for (t = key->text; t != NULL; t = t->next)
  547.         OutLine(t->line);        /* print text line */
  548. #endif
  549.     }
  550.  
  551.     ShowSubtopics(key, subtopics);
  552.     OutLine("\n");
  553.  
  554.     EndOutput();
  555. }
  556.  
  557. /* ShowSubtopics:
  558.  *  Print a list of subtopic names
  559.  */
  560. #define PER_LINE 4
  561.  
  562. /* the topic */
  563. /* (out) are there any subtopics */
  564. static void
  565. ShowSubtopics(KEY *key, boolean *subtopics)
  566. {
  567.     int subt = 0;            /* printed any subtopics yet? */
  568.     KEY *subkey;            /* subtopic key */
  569.     int len;                /* length of key name */
  570.     char line[BUFSIZ];        /* subtopic output line */
  571.     char *start;            /* position of subname in key name */
  572.     int sublen;            /* length of subname */
  573.     int pos = 0;
  574.     char *prev = NULL;        /* the last thing we put on the list */
  575.  
  576.     *line = '\0';
  577.     len = strlen(key->key);
  578.  
  579.     for (subkey = key+1; subkey->key != NULL; subkey++) {
  580.        if (strncmp(subkey->key, key->key, len) == 0) {
  581.           /* find this subtopic name */
  582.           start = subkey->key + len;
  583.           if (len > 0)
  584.             if (*start == ' ')
  585.              start++;        /* skip space */
  586.             else
  587.              break;        /* not the same topic after all  */
  588.           else            /* here we are looking for main topics */
  589.             if (!subkey->primary)
  590.              continue;    /* not a main topic */
  591.           sublen = instring(start, ' ');
  592.           if (prev == NULL || strncmp(start, prev, sublen) != 0) {
  593.              if (subt == 0) {
  594.                 subt++;
  595.                 if (len) {
  596.                   (void) sprintf(line, "Subtopics available for %s:\n",
  597.                   key->key);
  598.                 }
  599.                 else {
  600.                   (void) sprintf(line, "Help topics available:\n");
  601.                 }
  602.                 OutLine(line);
  603.                 sprintf(line, "    ");
  604.                 pos = 0;
  605.              }
  606.              if (pos == PER_LINE) {
  607.                 (void) strcat(line, "\n");
  608.                 OutLine(line);
  609.                 sprintf(line, "    ");
  610.                 pos = 0;
  611.              }
  612.              {
  613.                 char tmp[80];
  614.  
  615.                  /*********
  616.                  (void) strcat(line, "\t");
  617.                  (void) strncat(line, start, sublen);
  618.                  ************/
  619.                  (void) strncpy(tmp, start, sublen);
  620.                 tmp[sublen] = '\0';
  621.                  sprintf(line+strlen(line), "%-16s", tmp);
  622.              }
  623.              pos++;
  624.              prev = start;
  625.           }
  626.        } else {
  627.           /* new topic */
  628.           break;
  629.        }
  630.     }
  631.    
  632.     /* put out the last line */
  633.     if (subt > 0 && pos > 0) {
  634.        (void) strcat(line, "\n");
  635.        OutLine(line);
  636.     }
  637.    
  638. /*
  639.     if (subt == 0) {
  640.        OutLine("\n");
  641.        OutLine("No subtopics available\n");
  642.     }
  643. */
  644.    
  645.     if (subtopics)
  646.      *subtopics = (subt != 0);
  647. }
  648.  
  649.  
  650. /* StartOutput:
  651.  * Open a file pointer to a pipe to user's $PAGER, if there is one,
  652.  * otherwise use our own pager.
  653.  */
  654. static void
  655. StartOutput(void)
  656. {
  657. #if !defined(MSDOS) && !defined(AMIGA)
  658.     /********
  659.     char *pager_name = getenv("PAGER");
  660.     char *pager_name = "more -c";
  661.     if (pager_name != NULL && *pager_name != '\0')
  662.     ******/
  663.     extern char Ft_Pager[];
  664.     extern FILE *popen(const char *, const char *);
  665.  
  666.     if (*Ft_Pager) {
  667.         if ((help_stream = popen(Ft_Pager, "w")) != (FILE *)NULL)  {
  668.             return;
  669.         }
  670.         fprintf(stderr, "Warning: Could not open pager %s.\n", Ft_Pager);
  671.     }
  672.  
  673.     help_stream = stderr;
  674.     /* fall through to built-in pager */
  675. #endif
  676.  
  677.     /* built-in pager */
  678.     pagelines = 0;
  679. }
  680.  
  681. /* write a line of help output  */
  682. /* line should contain only one \n, at the end */
  683. static void
  684. OutLine(char *line)
  685. {
  686.     int c;                /* dummy input char */
  687. #if !defined(MSDOS) && !defined(AMIGA)
  688.     if (help_stream != stderr) {
  689.        fputs(line, help_stream);
  690.        return;
  691.     }
  692. #endif
  693.  
  694.     /* built-in dumb pager */
  695.     /* leave room for prompt line */
  696.     if (pagelines >= SCREENSIZE - 2) {
  697.        printf("Press return for more: ");
  698.        do
  699.         c = getchar();
  700.        while (c != EOF && c != '\n');
  701.        pagelines = 0;
  702.     }
  703.     fputs(line, stderr);
  704.     pagelines++;
  705. }
  706.  
  707. static void
  708. EndOutput(void)
  709. {
  710. #if !defined(MSDOS) && !defined(AMIGA)
  711.     extern int pclose(FILE *);
  712.  
  713.     if (help_stream != stderr) {
  714.         (void) pclose(help_stream);
  715.     }
  716. #endif
  717. }
  718.  
  719. /* find char c in string str; return p such that str[p]==c;
  720.  * if c not in str then p=strlen(str)
  721.  */
  722. static int
  723. instring(char *str, char c)
  724. {
  725.     int pos = 0;
  726.  
  727.     while (str != NULL && *str != '\0' && c != *str) {
  728.        str++;
  729.        pos++;
  730.     }
  731.     return (pos);
  732. }
  733.  
  734. #define TRUE 1
  735. #define FALSE 0
  736. #include "fudgit.h"
  737. #include "command.h"
  738. extern char *Ft_nextline(char *prompt, int *eof);
  739.  
  740. /* Ft_help: (not VMS, although it would work)
  741.  * Give help to the user.
  742.  * It parses the command line into helpbuf and supplies help for that
  743.  * string. Then, if there are subtopics available for that key,
  744.  * it prompts the user with this string. If more input is
  745.  * given, Ft_help is called recursively, with the argument the index of
  746.  * null character in the string. Thus a more specific help can be
  747.  * supplied. This can be done repeatedly.
  748.  * If null input is given, the function returns, effecting a
  749.  * backward climb up the tree.
  750.  * David Kotz (David.Kotz@Dartmouth.edu) 10/89
  751.  * Modified by Martin-D. Lacasse (isaac@physics.mcgill.ca) 12/91
  752.  */
  753. /* The first call is done without the "help" string */
  754. int Ft_help(int argc, char **argv, char *l, Command *cmp)
  755. {
  756.     char helpbuf[LINESIZE];
  757.     char prompt[LINESIZE];
  758.     int main;       
  759.     int more_help;
  760.     int subtopics;            /* 0 if no subtopics for this topic */
  761.     char *cp1, *cp2;
  762.     int eof;
  763.     int nargc;
  764.     extern int Ft_Interact;
  765.     extern int Ft_iolevel(void);
  766.     extern int Ft_vgetargp(char *b, char **p, int type, char **add);
  767.     extern char *Ft_nextline(char *prompt, int *eof);
  768.  
  769.     if (!Ft_Interact || Ft_iolevel()) {
  770.         fprintf(stderr, "%s: Interactive use only.\n", cmp->fname);
  771.         return(ERRR);
  772.     }
  773.  
  774.     helpbuf[0] = prompt[0] = 0;
  775.  
  776.     if (argc > 1) { /* take the last arguments only  */
  777.         cp1 = l + strlen(argv[0]) + 1;
  778.         l[strlen(l)-1] = '\0';
  779.         main = 0;
  780.     }   
  781.     else {
  782.         cp1 = "";
  783.         main = 1;
  784.     }
  785.  
  786.     subtopics = 0;
  787.     more_help = 1;
  788.  
  789.     switch (help(cp1, HELPFILE, &subtopics)) {
  790.        case H_FOUND: {
  791.           sprintf(helpbuf, "%s", cp1);
  792.           do {
  793.              if (subtopics) {
  794.                 /* prompt for subtopic with current help string */
  795.                 if (main) {
  796.                   (void) strcpy(prompt,
  797.                   "(Type ? for available topics.)\nHelp topic: ");
  798.                 }
  799.                 else {
  800.                   (void) sprintf(prompt,
  801.                   "(Type ? for available subtopics.)\nSubtopic of %s: ",
  802.                   helpbuf);
  803.                 }
  804.                 do {
  805.                     cp2 = Ft_nextline(prompt, &eof);
  806.                 } while (cp2 == NULL);
  807.                 if (*cp2 == '?') {
  808.                     help(helpbuf, HELPFILE, &subtopics);
  809.                 }
  810.                 else if (*cp2 != '\n') {
  811.                     sprintf(prompt, "help %s %s", helpbuf, cp2);
  812.                     nargc = Ft_vgetargp(prompt, argv, NOTHING, 0);
  813.                     if (nargc > argc) {
  814.                           Ft_help(nargc, argv, l, cmp);
  815.                     }
  816.                 }
  817.                 else {
  818.                        more_help = FALSE;
  819.                 }
  820.              } else
  821.                more_help = FALSE;
  822.           } while(more_help);
  823.    
  824.           break;
  825.        }
  826.        case H_NOTFOUND: {
  827.           printf("Sorry, no help for '%s'\n", cp1);
  828.           break;
  829.        }
  830.        case H_ERROR: {
  831.           perror(HELPFILE);
  832.           break;
  833.        }
  834.        default: {        /* defensive programming */
  835.           fputs("Ft_help: Impossible case in switch.\n", stderr);
  836.           /* NOTREACHED */
  837.        }
  838.     }
  839.     return(0);
  840. }
  841.  
  842.